package cn.tuling.ribbon.rules;

import cn.tuling.ribbon.balancer.WeightedBalancer;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * 同版本服务调用，同集群优先
 */
@Slf4j
public class SameClusterPriorityWithVersionRule extends AbstractLoadBalancerRule {
    @Autowired
    private NacosDiscoveryProperties discoveryProperties;

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
        // nothing
    }

    @Override
    public Server choose(Object key) {

        List<Instance> theSameClusterNameAndTheSameVersionInstList = getTheSameClusterNameAndTheSameVersionInstances(discoveryProperties);

        // 声明被调用的实例
        Instance toBeChooseInstance;

        if (theSameClusterNameAndTheSameVersionInstList.isEmpty()) {
            toBeChooseInstance = crossClusterAndTheSameVersionInvoke(discoveryProperties);
        } else {
            toBeChooseInstance = WeightedBalancer.chooseInstanceByRandomWeight(theSameClusterNameAndTheSameVersionInstList);
            log.info("同集群同版本调用--->当前微服务所在集群:{},被调用微服务所在集群:{},当前微服务的版本:{},被调用微服务版本:{},Host:{},Port:{}",
                    discoveryProperties.getClusterName(), toBeChooseInstance.getClusterName(), discoveryProperties.getMetadata().get("current-version"),
                    toBeChooseInstance.getMetadata().get("current-version"), toBeChooseInstance.getIp(), toBeChooseInstance.getPort());
        }

        return new NacosServer(toBeChooseInstance);
    }

    private Instance crossClusterAndTheSameVersionInvoke(NacosDiscoveryProperties discoveryProperties) {
        // 获取相同版本的服务实例信息
        List<Instance> crossClusterAndTheSameVersionInstList = getCrossClusterAndTheSameVersionInstList(discoveryProperties);

        String currentClusterName = discoveryProperties.getClusterName();
        String currentVersion = discoveryProperties.getMetadata().get("current-version");

        Instance toBeChooseInstance;
        if (crossClusterAndTheSameVersionInstList.isEmpty()) {
            log.info("跨集群调用找不到对应合适的版本当前版本为:currentVersion:{}", currentVersion);
            throw new RuntimeException("找不到相同版本的微服务实例");
        }

        toBeChooseInstance = WeightedBalancer.chooseInstanceByRandomWeight(crossClusterAndTheSameVersionInstList);
        log.info("跨集群同版本调用--->当前微服务所在集群:{},被调用微服务所在集群:{},当前微服务的版本:{},被调用微服务版本:{},Host:{},Port:{}",
                currentClusterName, toBeChooseInstance.getClusterName(), discoveryProperties.getMetadata().get("current-version"),
                toBeChooseInstance.getMetadata().get("current-version"), toBeChooseInstance.getIp(), toBeChooseInstance.getPort());

        return toBeChooseInstance;
    }

    private List<Instance> getCrossClusterAndTheSameVersionInstList(NacosDiscoveryProperties discoveryProperties) {
        String currentVersion = discoveryProperties.getMetadata().get("current-version");

        List<Instance> allInstance = getAllInstance(discoveryProperties);

        List<Instance> crossClusterAndTheSameVersionInstList = new ArrayList<>();
        for (Instance instance : allInstance) {
            if (StringUtils.endsWithIgnoreCase(instance.getMetadata().get("current-version"), currentVersion)) {
                crossClusterAndTheSameVersionInstList.add(instance);
            }
        }

        return crossClusterAndTheSameVersionInstList;
    }

    private List<Instance> getTheSameClusterNameAndTheSameVersionInstances(NacosDiscoveryProperties discoveryProperties) {
        String currentClusterName = discoveryProperties.getClusterName();
        String currentVersion = discoveryProperties.getMetadata().get("current-version");

        List<Instance> allInstances = getAllInstance(discoveryProperties);

        List<Instance> theSameClusterNameAndTheSameVersionInstList = new ArrayList<>();
        for (Instance instance : allInstances) {
            if (StringUtils.endsWithIgnoreCase(instance.getClusterName(), currentClusterName)
                    && StringUtils.endsWithIgnoreCase(instance.getMetadata().get("current-version"), currentVersion)) {
                theSameClusterNameAndTheSameVersionInstList.add(instance);
            }
        }

        return theSameClusterNameAndTheSameVersionInstList;
    }

    private List<Instance> getAllInstance(NacosDiscoveryProperties discoveryProperties) {
        BaseLoadBalancer loadBalancer = (BaseLoadBalancer) getLoadBalancer();

        String invokedServiceName = loadBalancer.getName();

        NamingService namingService = discoveryProperties.namingServiceInstance();

        List<Instance> allInstances = null;
        try {
            allInstances = namingService.getAllInstances(invokedServiceName);
        } catch (NacosException e) {
            log.error("获取{}服务实例失败", invokedServiceName);
        }
        return allInstances;
    }
}
